﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TanmiaGrp.Basis
{
    public static class Mediator
    {
        #region Used Members

        private static Dictionary<Enum, List<object>> fn_dict = new Dictionary<Enum, List<object>>();
        private static Dictionary<Enum, List<object>> dict = new Dictionary<Enum, List<object>>();
        private static Dictionary<Enum, object> parameters = new Dictionary<Enum, object>();

        /// <summary>
        /// Get the value associated with the current token.
        /// </summary>
        /// <typeparam name="T">Type of value you try to get.</typeparam>
        /// <param name="token">Token that the value associated with.</param>
        /// <param name="value">When this method return true, the value of the associated key will be in e. Otherwise default value.</param>
        /// <returns>True, when the token has corresponding value. False, otherwise.</returns>
        public static bool TryReceive<T>(this Enum token, out T value)
        {
            if (parameters.ContainsKey(token))
            {
                value = (T)parameters[token];
                parameters.Remove(token);
                return true;
            }

            value = default(T);
            return false;
        }

        /// <summary>
        /// Send value corresponding to token.
        /// </summary>
        /// <typeparam name="T">Type of value you want to send.</typeparam>
        /// <param name="token">Token to associate the value with.</param>
        /// <param name="value">The value to send with token.</param>
        public static Enum Send<T>(this Enum token, T value)
        {
            parameters[token] = value;
            return token;
        }

        /// <summary>
        /// Attach the specified eventHandler to the EventName.
        /// If the EventName already fired before registration Firing will be done immediately.
        /// </summary>
        /// <typeparam name="T">Type of EventArgs parameter.</typeparam>
        /// <param name="token">EventName to attach the eventHandler to.</param>
        /// <param name="eventHandler">EventHandler method to be called.</param>
        public static void Register<T>(this Enum token, Action<T> eventHandler)
        {
            if (!dict.ContainsKey(token))
            {
                var list = new List<object>();
                list.Add(eventHandler);
                dict.Add(token, list);
            }
            else
            {
                bool found = false;
                foreach (Action<T> item in dict[token])
                    if (item.Method.ToString() == eventHandler.Method.ToString())
                        found = true;
                if (!found)
                    dict[token].Add(eventHandler);
            }

            T e = default(T);
            if (token.TryReceive<T>(out e))
                token.Fire<T>(e);
        }

        /// <summary>
        /// Fires all eventHandlers that are attached with the specified token.
        /// If no eventHandler has been registered yet, e parameter will be send view Messaging.
        /// </summary>
        /// <typeparam name="T">Type of the EventArgs parameter.</typeparam>
        /// <param name="token">EventName to fire all its attached eventHandlers.</param>
        /// <param name="e">EventArgs data to be passed to all eventHandlers.</param>
        /// <returns>True if eventHandlers have fired. False, otherwise.</returns>
        public static bool Fire<T>(this Enum token, T e)
        {
            bool retVal = false;

            if (dict.ContainsKey(token))
                foreach (Action<T> callback in dict[token].Where(x => x.GetType() == typeof(Action<T>)))
                {
                    callback(e);
                    retVal = true;
                }

            if (retVal == false)
                token.Send<T>(e);

            return retVal;
        }

        /// <summary>
        /// Call the Delegation function that is associated with the token.
        /// </summary>
        /// <typeparam name="T">Type of EventArgs parameter.</typeparam>
        /// <typeparam name="TResult">Type of result that will returned from Calling function.</typeparam>
        /// <param name="token">FunctionName to call its attached functions.</param>
        /// <param name="e">EventArgs data to be passed to functions.</param>
        /// <returns>The result of the function.</returns>
        public static TResult Call<T, TResult>(this Enum token, T e)
        {
            if (fn_dict.ContainsKey(token))
                foreach (Func<T, TResult> callback in fn_dict[token])
                    return callback(e);

            return default(TResult);
        }

        /// <summary>
        /// Attach the specified function to the FunctionName.
        /// </summary>
        /// <typeparam name="T">Type of EventArgs parameter.</typeparam>
        /// <typeparam name="TResult">Type of result data that is returned from function.</typeparam>
        /// <param name="token">FunctionName to attach the function to.</param>
        /// <param name="callback">Function method to be called.</param>
        public static void Register<T, TResult>(this Enum token, Func<T, TResult> callback)
        {
            if (!fn_dict.ContainsKey(token))
            {
                var list = new List<object>();
                list.Add(callback);
                fn_dict.Add(token, list);
            }
            else
            {
                bool found = false;
                foreach (Func<T, TResult> item in fn_dict[token])
                    if (item.Method.ToString() == callback.Method.ToString())
                        found = true;
                if (!found)
                    fn_dict[token].Add(callback);
            }
        }

        /// <summary>
        /// Unregister the eventHandler from calling list.
        /// </summary>
        /// <typeparam name="T">Type of EventArgs parameter.</typeparam>
        /// <param name="token">EventName to detach the eventHandler from.</param>
        /// <param name="eventHandler">EventHandler method to detach.</param>
        public static void Unregister<T>(this Enum token, Action<T> eventHandler)
        {
            if (dict.ContainsKey(token))
                dict[token].Remove(eventHandler);
        }

        /// <summary>
        /// Unregister the function from calling list.
        /// </summary>
        /// <typeparam name="T">Type of EventArgs parameter.</typeparam>
        /// <typeparam name="TResult">Type of result value.</typeparam>
        /// <param name="token">FunctionName to detach the function from.</param>
        /// <param name="function">Function method to detach.</param>
        public static void Unregister<T, TResult>(this Enum token, Func<T, TResult> function)
        {
            if (fn_dict.ContainsKey(token))
                fn_dict[token].Remove(function);
        }

        #endregion Used Members
    }
}
